/*
 * This file is part of LiquidBounce (https://github.com/CCBlueX/LiquidBounce)
 *
 * Copyright (c) 2016 - 2021 CCBlueX
 *
 * LiquidBounce is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * LiquidBounce is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with LiquidBounce. If not, see <https://www.gnu.org/licenses/>.
 */

package net.ccbluex.liquidbounce.features.module.modules.exploit

import net.ccbluex.liquidbounce.config.Choice
import net.ccbluex.liquidbounce.config.ChoiceConfigurable
import net.ccbluex.liquidbounce.event.PacketEvent
import net.ccbluex.liquidbounce.event.handler
import net.ccbluex.liquidbounce.event.repeatable
import net.ccbluex.liquidbounce.features.module.Category
import net.ccbluex.liquidbounce.features.module.Module
import net.ccbluex.liquidbounce.utils.client.timer
import net.minecraft.network.packet.c2s.play.KeepAliveC2SPacket
import net.minecraft.network.packet.c2s.play.PlayerActionC2SPacket
import net.minecraft.network.packet.c2s.play.PlayerMoveC2SPacket
import net.minecraft.network.packet.s2c.play.KeepAliveS2CPacket
import org.apache.commons.lang3.RandomUtils
import java.util.concurrent.ConcurrentLinkedQueue

/**
 * A disabler module
 *
 * Disables either a specific anticheat or server.
 */

object ModuleDisabler : Module("Disabler", Category.EXPLOIT) {

    private val modes = choices(
        "Mode",
        MineplexCombat,
        arrayOf(
            Hypixel,
            RedeskyOld,
            MineplexCombat,
            AACOld,
            VerusCombat
        )
    )

    // Hypixel disabler
    private object Hypixel : Choice("Hypixel") {

        override val parent: ChoiceConfigurable
            get() = modes

        private var ids = ConcurrentLinkedQueue<Short>()

        val repeatable = repeatable { // todo: might use world change event
            if (mc.world == null) {
                ids.clear()
            }
        }

        override fun disable() {
            ids.clear()
        }

        val packetHandler = handler<PacketEvent> { event ->
            when (val packet = event.packet) {
//                is ConfirmScreenActionC2SPacket -> {
//                    if (packet.actionId < 0 && packet.syncId == 0 && mc.player != null && mc.world != null) {
//                        if (!ids.isEmpty()) {
//                            network.sendPacket(ConfirmScreenActionC2SPacket(0, ids.poll(), true))
//                        } else {
//                            ids.offer(packet.actionId)
//                            event.cancelEvent()
//                        }
//                    }
//                }
                is KeepAliveS2CPacket -> event.cancelEvent()
            }
        }
    }

    // Redesky disabler (Old?)
    private object RedeskyOld : Choice("RedeskyOld") {

        override val parent: ChoiceConfigurable
            get() = modes

        private var startYaw = 0f
        private var startPitch = 0f

        override fun enable() {
            startYaw = player.yaw
            startPitch = player.pitch
        }

        override fun disable() {
            mc.timer.timerSpeed = 1f
        }

        val repeatable = repeatable {
            mc.timer.timerSpeed = 0.12f
        }

        val packetHandler = handler<PacketEvent> { event ->
            val packet = event.packet

            if (packet is PlayerMoveC2SPacket) {
                val randomFloat = RandomUtils.nextFloat(10.1f, 50f)

                if (player.ticksSinceLastPositionPacketSent % 4 == 0) {
                    packet.x += if (RandomUtils.nextBoolean()) randomFloat else -randomFloat
                    packet.y += if (RandomUtils.nextBoolean()) randomFloat else -randomFloat
                    packet.z += if (RandomUtils.nextBoolean()) randomFloat else -randomFloat
                }

                val lastReportedYaw = player.lastYaw
                val lastReportedPitch = player.lastPitch

                if (packet.changeLook) {
                    packet.yaw = if (player.ticksSinceLastPositionPacketSent % 2 == 0) startYaw else lastReportedYaw
                    packet.pitch =
                        if (player.ticksSinceLastPositionPacketSent % 2 == 0) startPitch else lastReportedPitch
                }
            }
        }
    }

    // Mineplex Combat disabler
    private object MineplexCombat : Choice("MineplexCombat") {

        override val parent: ChoiceConfigurable
            get() = modes

        val packetHandler = handler<PacketEvent> { event ->
            if (event.packet is KeepAliveC2SPacket) {
                event.packet.id -= RandomUtils.nextInt(1000, 2147483647)
            }
        }
    }

    // AAC 1.9.10 disabler
    private object AACOld : Choice("AAC1.9.10") {

        override val parent: ChoiceConfigurable
            get() = modes

        val packetHandler = handler<PacketEvent> { event ->
            if (event.packet is PlayerMoveC2SPacket) {
//                network.sendPacket(PlayerInputC2SPacket())
                event.packet.y += 7.0E-9
            }
        }
    }

    // Verus Combat disabler
    private object VerusCombat : Choice("VerusCombat") {

        override val parent: ChoiceConfigurable
            get() = modes

        private var transactionPackets = 0

        val packetHandler = handler<PacketEvent> { event ->
            when (val packet = event.packet) {
//                is ConfirmScreenActionC2SPacket -> {
//                    if (player.isDead) {
//                        transactionPackets = 0
//                    }
//                    if (transactionPackets != 0) {
//                        event.cancelEvent()
//                    } else {
//                        transactionPackets++
//                    }
//                }
                is PlayerActionC2SPacket -> event.cancelEvent()
            }
        }
    }

}
